package vip.aster.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import vip.aster.common.constant.Constants;
import vip.aster.common.constant.enums.SuperAdminEnum;
import vip.aster.framework.security.entity.SecurityUser;
import vip.aster.framework.security.entity.UserDetail;
import vip.aster.system.entity.SysMenu;
import vip.aster.system.entity.SysTenant;
import vip.aster.system.entity.SysTenantPackage;
import vip.aster.system.mapper.SysMenuMapper;
import vip.aster.system.mapper.SysTenantMapper;
import vip.aster.system.mapper.SysTenantPackageMapper;
import vip.aster.system.query.SysMenuQuery;
import vip.aster.system.service.SysMenuService;
import vip.aster.system.vo.SysMenuVO;
import vip.aster.tenant.constants.TenantConstants;
import vip.aster.tenant.utils.TenantUtils;

import java.util.*;

/**
 * <p>
 * 菜单 服务实现类
 * </p>
 *
 * @author Aster
 * @since 2023-11-28 10:36
 */
@Service
@AllArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
    private SysMenuMapper sysMenuMapper;
    private SysTenantMapper tenantMapper;
    private SysTenantPackageMapper tenantPackageMapper;

    @Override
    public Set<String> getUserAuthority(UserDetail user) {
        List<String> authorityList;
        if (SuperAdminEnum.YES.getCode().equals(user.getSuperAdmin())) {

            authorityList = this.list(getWrapper(new SysMenuQuery())).stream().map(SysMenu::getPerms).toList();
        } else {
            authorityList = sysMenuMapper.getUserAuthorityList(user.getId());
        }
        // 用户权限列表
        Set<String> permsSet = new HashSet<>();
        for (String authority : authorityList) {
            if (StrUtil.isBlank(authority)) {
                continue;
            }
            permsSet.addAll(Arrays.asList(authority.trim().split(",")));
        }
        return permsSet;
    }

    @Override
    public List<SysMenuVO> treeList(SysMenuQuery query) {
        // 先查出所有符合条件的菜单
        List<SysMenu> list = this.list(getWrapper(query));

        boolean limitMenu = false;
        // 1.判断是否开启租户
        if (TenantUtils.isEnable()) {
            // 2.判断是否超级管理员
            if (SecurityUser.isSuperAdmin()) {
                // 3.若是超级管理员,但不是默认租户,则只显示租户套餐内的菜单
                limitMenu = true;
            } else if (!TenantConstants.DEFAULT_TENANT_ID.equals(TenantUtils.getTenantId())) {
                // 4.若不是超级管理员且不是默认租户,则只返回租户套餐内的菜单
                limitMenu = true;
            }
        }
        if (limitMenu) {
            String tenantId = TenantUtils.getTenantId();
            SysTenant tenant = tenantMapper.selectById(tenantId);
            if (tenant != null && StrUtil.isNotBlank(tenant.getPackageId())) {
                SysTenantPackage tenantPackage = tenantPackageMapper.selectById(tenant.getPackageId());
                if (ObjectUtil.isNotEmpty(tenantPackage) && StrUtil.isNotBlank(tenantPackage.getMenuIds())) {
                    List<String> menuIds = StrUtil.split(tenantPackage.getMenuIds(), ",");
                    list = list.stream().filter(m -> menuIds.contains(m.getId())).toList();
                }
            }
        }

        List<SysMenuVO> menuList = SysMenuVO.convertList(list);

        return treeBuild(menuList, Constants.ROOT_NODE);
    }

    @Override
    public void save(SysMenuVO entity) {
        SysMenu menu = entity.reconvert();
        if (StrUtil.isNotBlank(menu.getId())) {
            sysMenuMapper.updateById(menu);
        } else {
            sysMenuMapper.insert(menu);
        }
    }

    @Override
    public List<SysMenuVO> getUserMenuList(UserDetail user, List<String> types) {

        List<SysMenu> menuList;

        // 系统管理员，拥有最高权限
        if (SuperAdminEnum.YES.getCode().equals(user.getSuperAdmin())) {
            LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(CollUtil.isNotEmpty(types), SysMenu::getMenuType, types);
            queryWrapper.orderByAsc(SysMenu::getSort);
            menuList = this.list(queryWrapper);
        } else {
            menuList = sysMenuMapper.getUserMenuList(user.getId(), types);
            // 多角色的情况下有可能会有重复数据，需要去重
            if (CollUtil.isNotEmpty(menuList)) {
                menuList = menuList.stream().distinct().toList();
            }
        }

        return treeBuild(SysMenuVO.convertList(menuList), Constants.ROOT_NODE);
    }

    private LambdaQueryWrapper<SysMenu> getWrapper(SysMenuQuery query) {
        LambdaQueryWrapper<SysMenu> wrapper = Wrappers.lambdaQuery();

        wrapper.like(StrUtil.isNotBlank(query.getName()), SysMenu::getName, query.getName());
        wrapper.like(StrUtil.isNotBlank(query.getPath()), SysMenu::getMenuPath, query.getPath());
        wrapper.eq(StrUtil.isNotBlank(query.getStatus()), SysMenu::getStatus, query.getStatus());
        wrapper.orderByAsc(SysMenu::getSort);

        return wrapper;
    }

    private List<SysMenuVO> treeBuild(List<SysMenuVO> nodes, String rootNode) {
        List<SysMenuVO> list = new ArrayList<>();
        long count = nodes.stream().filter(m -> rootNode.equals(m.getPid())).count();
        if (count == 0L) {
            return nodes;
        }
        for (SysMenuVO node : nodes) {
            // 从root节点开始组合树
            if (rootNode.equals(node.getPid())) {
                node.setChildren(getChildren(node.getId(), nodes));
                list.add(node);
            }
        }
        return list;
    }

    private List<SysMenuVO> getChildren(String id, List<SysMenuVO> nodes) {
        List<SysMenuVO> list = new ArrayList<>();
        for (SysMenuVO node : nodes) {
            if (node.getPid().equals(id)) {
                node.setChildren(getChildren(node.getId(), nodes));
                list.add(node);
            }
        }
        return list;
    }
}
